home *** CD-ROM | disk | FTP | other *** search
- /*
- Dragon.c
-
- Simple framework for a drag-n-drop application. A "dragon" does something meaningful (one
- would hope) with the Finder objects that were drag-n-dropped onto it, then immediately quits.
-
- This class does nothing by itself; simple subclasses will need to override ProcessDroppings
- ╤ more powerful subclasses may need to much more (e.g., override event handling methods).
-
- Developed using THINK C 5.0 and ResEdit 2.1.1
-
- Copyright ⌐ 1992 by Paul M. Hoffman
- Send feedback to paul.hoffman@um.cc.umich.edu
-
- This code may be freely used, altered, and distributed in any way you want as long as:
- 1. It is GIVEN away rather than sold;
- 2. This statement and the above copyright notice are left intact.
-
- Created 09 Apr 1992 v0.1.1 First attempt. Brings up the alert OK but crashes with a bus
- error in the 'PACK' 8 code executed after return from
- HandleOdoc
- Modified 11 Apr 1992 v0.1.2 No improvement
- v0.1.3 First working code. I think the problem was that HandleOdoc
- was returning an invalid error ╤ it was calling _DisposPtr
- on a free block, which produces error -111, which is not
- a valid AE error ╤ but why the hell should this cause a
- bus error???
- Discovered that 'oapp' is sent ONLY if nothing was
- drag-n-dropped; fixed HandleOapp accordingly (v0.1.3b
- and v0.1.3c)
- 12 Apr 1992 v0.2.0 Extracted ProcessDroppings. You can now make "plug-in
- dragons" by following the instructions in the file
- ProcessDroppings.h. This setup should keep recompile
- times low
- Memory management could be improved by making a call
- to TempNewHandle and HLock instead of NewPtr
- Overall error handling needs to be worked on. I haven't even
- tested Error () and Abort () to make sure they work!
- Implemented testing using TestDragon.c
- v0.2.1 Fixed totally stupid sizeof (FSSpec*) when what I needed was
- sizeof (FSSpec)
- 14 Apr 1992 v0.2.2 We now pass an FSSpec ** to ProcessDroppings instead of an
- FSSpec * ╤ and the block is not locked
- 24 Apr 1992 OOPSied ╤ we're dealing with objects now!
- Incorporated TestDragon.c ╤ what the hell, let compile times
- suffer all they want!
- 30 Apr 1992 Added autoQuit instance variable
- Added stubs for menus
- Added DoOapp and DoOdoc methods and gave them the code
- that was in HandleOapp and HandleOdoc
- 01 May 1992 v0.2.3 Completely rewrote Test method to use ResEdit files with 'alis'
- resources (which can be created using DalisMaker) ╤
- with the old Test, ProcessDroppings could never get an
- FSSpec to a folder or volume
- 05 May 1992 v0.2.4 Added a check for 'FREF' resources in the .rsrc file, so Test
- won't pass FSSpec's for any verboten types of Finder
- objects. For example, what if you're testing a dragon that
- will, when finished, strip the resource fork from 'TEXT' files
- drag-and-dropped on it ╤ and while testing you stupidly
- let ProcessDroppings get a hold of an FSSpec to your
- favorite golf game ╤ AAAAAIIIIGGGGHHHHHHH!!!!!!!!)
- Moved functions from #ifdef DEBUG section to FileUtils.c
- 12 May 1992 Fixed stupidity with InitMenus ╤ renamed method to
- SetUpMenus (duh!) and moved to proper place (ack!)
- 14 May 1992 v0.3.0 Added fuller event handling (mostly empty methods) and tidied
- things up a bit in preparation for first release
- 17 May 1992 v0.3.1 When run in the THINK C Debugger, we get an 'oapp' event but
- calling AEProcessAppleEvent doesn't work ╤ it returns
- an error of -608 (noOutstandingHLE). This means we have
- to keep the call to Test in Run rather than moving it to
- DoOapp, where it seems to belong! Boy, was I confused!
- OK, I should've realized DoOdoc has to call SetUpMenus if
- menusInstalled == FALSE. Duh!
- 17 May 1992 v1.0b1 First release
- 20 May 1992 v1.0b2 Second release ╤ more comments, improved File Paths dragon
- */
-
- #include "Dragon.h"
-
- Dragon::Dragon (void)
- {
- autoQuit = TRUE; // A subclass's constructor (which will be called AFTER this one)
- // should set autoQuit to FALSE instead if it hangs around after
- // starting up ╤ see DoOapp for the reason why
- menusInstalled = FALSE; // No menus ╤ but maybe a subclass will override us (ooh!)
- }
-
- void Dragon::Start (void)
- {
- InitMac ();
- InitMilieu ();
- InitMem ();
- }
-
- void Dragon::InitMac (void)
- {
- MaxApplZone ();
- InitGraf (&thePort);
- InitFonts ();
- InitWindows ();
- InitMenus ();
- TEInit ();
- InitDialogs (NULL);
- InitCursor ();
- }
-
- void Dragon::InitMilieu (void)
- {
- OSErr err;
- long result;
- Boolean hasAppleEvents;
-
- err = Gestalt (gestaltAppleEventsAttr, &result);
- hasAppleEvents = result & 1L; // If bit 0 ( == gestaltAppleEventsPresent) is set, we have 'em!
- if (hasAppleEvents)
- InitAppleEvents ();
- else
- Abort (eNoAppleEvents);
- }
-
- void Dragon::InitAppleEvents (void)
- {
- OSErr err;
-
- if ((err = AEInstallEventHandler (kCoreEventClass, kAEOpenApplication, HandleOapp, 0, FALSE))
- || (err = AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments, HandleOdoc, 0, FALSE))
- || (err = AEInstallEventHandler (kCoreEventClass, kAEPrintDocuments, HandlePdoc, 0, FALSE))
- || (err = AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, HandleQuit, 0, FALSE))
- )
- Abort (eCouldntInstallAppleEvents);
- }
-
- void Dragon::InitMem (void)
- {
- CallMoreMasters ();
- }
-
- void Dragon::CallMoreMasters (void)
- {
- short mmCalls, **mmCallsHandle;
-
- // Call MoreMasters the number of times specified in the 'MoMa' resource ╤ each call gives us
- // an extra 64 (?) master pointers
- mmCallsHandle = (short **) GetResource ('MoMa', 128);
- if (mmCallsHandle == NULL) // If there is no 'MoMa' resource, call it a few times
- mmCalls = 3;
- else
- mmCalls = **mmCallsHandle;
- for ( ; mmCalls > 0; mmCalls--)
- MoreMasters ();
- }
-
- void Dragon::SetUpMenus (void)
- {
- // Override this method if you actually WANT menus (imagine!)
- // Make sure you set menusInstalled = TRUE after you install
- // your menus ╤ otherwise, DoMenu will never get called!
- // It's also a good idea to abort your SetUpMenus method if
- // menusInstalled == TRUE ╤ just in case there's some
- // weirdness with Apple Events while debugging (Q: What
- // would happen if Test and DoOapp were both called, and
- // we didn't make this check? ╤ A: The subclass's
- // SetUpMenus would be called twice ╤ crashola!)
- }
-
- void Dragon::Run (void)
- {
- #define kSleepTime 6 // Sleep time should be in the neighborhood of 2 < n < 16
- // A better idea is to store the sleep time in a resource, but
- // that seems like overkill here. (You could always
- // override this method if you wanted to do it that way.)
-
- Boolean gotEvent;
- EventRecord event;
-
- running = TRUE;
-
- #ifdef DEBUG
- Test (); // If we're debugging, we have to run through the test now ╤ we'll never get an 'oapp'
- // or 'odoc' event
- // VERY IMPORTANT NOTE:
- // Actually, we DO get 'oapp' but when we call AEProcessAppleEvent, we get an
- // error of -608 == noOutstandingHLE (#!$@#?!). I don't understand this at all, but
- // calling Test here seems to work just fine, so ╔
- #endif
-
- while (running) {
- gotEvent = WaitNextEvent (everyEvent, &event, kSleepTime, NULL);
- if (gotEvent)
- DoEvent (&event);
- else
- DoIdle ();
- }
- }
-
- OSErr Dragon::ProcessDroppings (FSSpec **docs, long numDocs)
- {
- // Override this method, or your dragon won't do nothin'
-
- DisposHandle ((Handle) docs);
- }
-
- #ifdef DEBUG // This should be defined in the project prefix while testing and debugging
-
- // You won't get any Apple Events while debugging in THINK C (unless you do some
- // fancy stuff in another application) ╤ so the Run method calls Test before it
- // enters the main loop.
-
- // NOTE: An application launched under System 7 will get either an 'oapp' or 'odoc'
- // event, not both! Inside Macintosh doesn't say it, but it IMPLIES it ╤ and
- // experience proves it
-
- // This means that Test must have the same functionality (with perhaps a little less
- // error checking) as DoOdoc AND DoOapp. Specifically ╤
- // 1. Test must pass a Handle to some FSSpecs to ProcessDroppings and
- // finish by testing autoQuit and acting accordingly. It gets these FSSpecs
- // by resolving aliases ('alis' resources) read from a file ╤ you can make the
- // file with the "alisMaker" dragon (see the read-me file in its folder).
- // 2. It also means that Test must NOT pass FSSpecs which CAN'T be
- // drag-and-dropped on the finished dragon! Therefore we check for 'FREF'
- // resources in the project's .rsrc file ╤ that's essentially what the System
- // does in the instant it takes it to decide whether to allow drag-and-drop.
- // Therefore you should have at least one 'FREF' resource in your .rsrc file,
- // or Test won't do nuttin', honey! (Actually, you should have two ╤ the first
- // one is for type 'APPL' and is needed to get the Finder to show your icon.)
- // 3. It must call SetUpMenus if autoQuit == FALSE
-
- // Of course, what we REALLY need is a way to feed Apple Events to the project when
- // it's running in the THINK C Debugger
-
- void Dragon::Test (void)
- {
- FSSpec **fssHndl, *fss;
- StandardFileReply reply;
- OSType resEdType = 'rsrc', **fileTypesHndl;
- short refNum, numAlises, i, numFSSpecs, numFileTypes;
- Boolean wasChanged;
- OSErr err;
- AliasHandle alisHndl;
-
- // Get a list of file types that this dragon can open from the 'FREF' resources in the project's
- // .rsrc file ╤ we call this first so Count1Resources and Get1Resource will work correctly
- // below. This is because whereas NOW the most recently opened resource fork is from
- // the .rsrc file, this will NOT be the case a little later when we open the 'alis' resources file
- fileTypesHndl = FREFTypes (&numFileTypes);
- if (fileTypesHndl == NULL)
- return;
-
- // Pick a ResEdit file to get the 'alis' resources from
- StandardGetFile (NULL, 1, &resEdType, &reply);
- if (reply.sfGood) {
-
- // Open the resource fork of the file chosen and find out how many 'alis' resources there are
- refNum = FSpOpenResFile (&reply.sfFile, fsRdPerm);
- err = ResError ();
- if (err == noErr) {
- numAlises = Count1Resources ('alis');
-
- // Allocate a block for the FSSpecs that we'll end up passing to ProcessDroppings
- fssHndl = (FSSpec **) AnyHandle (numAlises * sizeof (FSSpec));
- if (fssHndl != NULL) {
- MoveHHi ((Handle) fssHndl); // Move the block up out of the way and
- HLock ((Handle) fssHndl); // lock it for fast dereferencing later
-
- // Go through the 'alis' resources and (attempt to) resolve each one to an FSSpec
- // ╤ when the loop finishes, numFSSpecs will have been set to the number of things
- // we're going to pass to ProcessDroppings
- for (i = 1, fss = *fssHndl, numFSSpecs = 0; i <= numAlises; i++) {
- alisHndl = (AliasHandle) Get1IndResource ('alis', i);
- if (alisHndl != NULL) {
-
- // Convert the 'alis' resource to an FSSpec record (pointed to by fss)
- // ╤ we ignore the value returned in wasChanged
- err = ResolveAlias (NULL, alisHndl, fss, &wasChanged);
- if (err != noErr) { // Skip to the next iteration of the for loop if
- continue; // we couldn't resolve the 'alis' resource
- }
-
- // Now check to make sure fss contains a digestible FSSpec
- if (FSpOpenableType (fss, numFileTypes, fileTypesHndl)) {
- fss++;
- numFSSpecs++;
- }
- DisposHandle ((AliasHandle) alisHndl); // Don't forget to dispose of the block!
- }
- }
- HUnlock ((Handle) fssHndl);
- FSClose (refNum);
- if (numFSSpecs < numAlises) // Waste not, want not╔
- SetHandleSize ((Handle) fssHndl, numFSSpecs * sizeof (FSSpec));
- (void) ProcessDroppings (fssHndl, numFSSpecs);
- } else
- FSClose (refNum);
- } // if (err == noErr)
- } // if (reply.sfGood)
-
- if (autoQuit) // Should we quit immediately?
- StopRunning ();
- else
- SetUpMenus ();
- }
-
- #endif DEBUG
-
- void Dragon::DoEvent (EventRecord *event)
- {
- switch (event->what) {
- case mouseDown:
- DoMouseDown (event);
- break;
- case mouseUp:
- DoMouseUp (event);
- break;
- case keyDown:
- case autoKey:
- DoKeyDown (event);
- break;
- case activateEvt:
- DoActivate (event);
- break;
- case updateEvt:
- DoUpdateEvent (event);
- break;
- case diskEvt:
- DoDiskInsert (event);
- case osEvt: // Suspend, resume, and mouse-moved events
- DoOSEvent (event);
- break;
- case kHighLevelEvent:
- DoHighLevelEvent (event);
- break;
- }
- }
-
- void Dragon::DoMouseDown (EventRecord *theEvent)
- {
- WindowPtr whichWindow;
- short domain;
-
- domain = FindWindow (theEvent->where, &whichWindow);
- switch (domain) {
- case inSysWindow:
- SystemClick (theEvent, whichWindow);
- break;
- case inMenuBar:
- // Watch out for stray mouseDowns in non-existent menus ╔
- if (menusInstalled)
- DoMenu (MenuSelect (theEvent->where));
- break;
- default:
- break;
- }
- }
-
- void Dragon::DoMouseUp (EventRecord *theEvent)
- {
- // Do nothing
- }
-
- void Dragon::DoKeyDown (EventRecord *theEvent)
- {
- long ticks;
-
- if ((theEvent->modifiers & cmdKey) && menusInstalled) // If we have menus, handle
- DoMenu (MenuKey (theEvent->message)); // cmd-key combos
- }
-
- void Dragon::DoActivate (EventRecord *theEvent)
- {
- // Null method ╤ override if you have windows
- }
-
- void Dragon::DoUpdateEvent (EventRecord *theEvent)
- {
- // Null method ╤ override if you have windows
- }
-
- void Dragon::DoDiskInsert (EventRecord *theEvent)
- {
- Point pt = {100, 100}; // Would { -1, -1 } give us a centered dialog??
-
- if ((short) (theEvent->message >> 16) != noErr) // If the disk isn't formatted (or has problems),
- (void) DIBadMount (pt, theEvent->message); // give the user the opportunity to initialize it
- }
-
- void Dragon::DoOSEvent (EventRecord *theEvent)
- {
- switch ((theEvent->message >> 24) & 0x00FF) { // High byte tells us what kind of event it is
- case suspendResumeMessage:
- if (theEvent->message & resumeFlag)
- DoResume ();
- else
- DoSuspend ();
- break;
- case mouseMovedMessage:
- break;
- default:
- break;
- }
- }
-
- void Dragon::DoHighLevelEvent (EventRecord *theEvent)
- {
- short err;
-
- // We assume all high level events are Apple Events
- err = AEProcessAppleEvent (theEvent);
- }
-
- void Dragon::DoMenu (long menuItemCode)
- {
- // Override this method if you need menus
- // Make sure you set menusInstalled in
- // SetUpMenus, or you'll never get here!
- }
-
- void Dragon::DoIdle (void)
- {
- // Override this to do periodic actions
- }
-
- void Dragon::DoSuspend (void)
- {
- // Override this to do something special when the dragon is switched out
- }
-
- void Dragon::DoResume (void)
- {
- // Override this to do something special when the dragon is switched back in
- }
-
- void Dragon::StopRunning (void)
- {
- running = FALSE;
- }
-
- void Dragon::Finish (void)
- {
- /* What is there to do?
- Release any temporary memory (I don't know if the system will do this for you)
- Delete any temporary files
- Remove any trap patches
- etc. (look at CApplication.c in the THINK Class Library for some ideas)
- */
- }
-
- pascal OSErr HandleOapp (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
- {
- // NOTE: We don't get an 'oapp' event unless nothing was drag-n-dropped ╤ this is
- // never explicitly stated in Inside Macintosh vol. 6 but seems clearly to be true
- // (and it makes a lot of more sense than if BOTH events were sent)
- // A better way to do things would be to stuff a reference to the dragon object in the event
- // handler's refCon. This would essentially eliminate the need for the gDragon global ╔
-
- OSErr err;
-
- err = gDragon->DoOapp (theAppleEvent, theReply, refCon);
- return err;
- }
-
- pascal OSErr HandleOdoc (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
- {
- OSErr err;
-
- err = gDragon->DoOdoc (theAppleEvent, theReply, refCon);
- return err;
- }
-
- pascal OSErr HandlePdoc (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
- {
- return noErr; // Ignore 'pdoc' events ╤ most subclasses won't need to override
- }
-
- pascal OSErr HandleQuit (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
- {
- gDragon->StopRunning (); // This'll keep us from going through the main event loop again
- return noErr;
- }
-
- OSErr Dragon::DoOapp (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
- {
- // Override this method if you want your dragon do something when it's double-clicked
-
- // The default SetUpMenus doesn't do anything ╤ this is just here so a subclass can override
- // SetUpMenus and call inherited::DoOapp in its DoOapp method. It seemed to make sense
- // at the time ╔
-
- if (autoQuit) // Should we quit immediately? ╤ autoQuit will normally be set in
- // your dragon's constructor method (here, Dragon::Dragon)
- StopRunning ();
- else
- SetUpMenus (); // If we don't quit right after drag-and-dropping, then we'd better
- // have some menus to let the poor user do whatever it is we
- // expect them to do after we hang around (like quit, at least!)
- }
-
- OSErr Dragon::DoOdoc (AppleEvent *theAppleEvent, AppleEvent *theReply, long refCon)
- {
- // You probably won't need to override this method ╤ it calls ProcessDroppings with information
- // gleaned from the Apple Event. Then again, you could filter out folders here rather than in
- // ProcessDroppings
-
- register OSErr err;
- AEDescList docList;
- long i, numDocs, actualSize;
- FSSpec *fss;
- AEKeyword keyword;
- DescType returnedType;
- FSSpec **droppings = NULL; // FSSpecs of Finder objects dragged-n-dropped
-
- err = AEGetParamDesc (theAppleEvent, keyDirectObject, typeAEList, &docList);
- if (err)
- return err;
-
- // OK, docList contains a valid allocated AEDescList ╤ now get file specs from it
-
- // First, make sure nothing's there that we didn't expect ╤ this is a formality for 'odoc' events
- err = GotRequiredParams (theAppleEvent);
- if (err == noErr) {
-
- // How many files/folders are there?
- err = AECountItems (&docList, &numDocs);
-
- // I don't think numDocs can be <= 0, but ╔
- if (err == noErr && numDocs > 0) {
- // Allocate a relocatable block ╤ use temporary memory if necessary
- droppings = (FSSpec **) AnyHandle (numDocs * sizeof (FSSpec));
- if (droppings != NULL) {
- HLock ((Handle) droppings);
- for (i = 1, fss = *droppings; i <= numDocs; i++, fss++) {
- err = AEGetNthPtr (&docList, i, typeFSS, &keyword,
- &returnedType, (Ptr) fss, sizeof (FSSpec), &actualSize);
- if (err) break;
- }
- HUnlock ((Handle) droppings);
-
- // => This is where the whole shebang gets called!
-
- err = ProcessDroppings (droppings, numDocs);
-
- // NOTE: ProcessDroppings disposes of droppings
- // (can you say "draggie bag"?)
- }
- }
- }
-
- // All done ╤ clean up (we don't get here if docList wasn't successfully extracted)
- (void) AEDisposeDesc (&docList);
-
- if (autoQuit) // Should we quit immediately after handling this event?
- StopRunning ();
- else if (!menusInstalled) // We'll set up our menus here if (and only if) they haven't
- SetUpMenus (); // already been installed
- return err;
- }
-
- OSErr GotRequiredParams (AppleEvent *theEvent)
- {
- DescType returnedType;
- long actualSize;
- OSErr err;
-
- err = AEGetAttributePtr (theEvent, keyMissedKeywordAttr, typeWildCard, &returnedType, NULL, 0, &actualSize);
- if (err == errAEDescNotFound)
- return noErr;
- else if (err == noErr)
- return errAEEventNotHandled;
- else
- return err;
- }
-
- void Dragon::Abort (short errNum)
- {
- Error (errNum);
- Finish ();
- ExitToShell (); // Sloppy, but it works
- }
-
- void Dragon::Error (short errNum)
- {
- Str255 string;
-
- NumToString (errNum, &string);
- ParamText (&string, "\p", "\p", "\p");
- Alert (rErrorAlert, NULL);
- }